﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

using TTServer.Utilities;
using TTServer.Messages;
using TTServer.Objects;

//using libSecuritySystem;
using libCommonClass;
using libUtilities;
using libInterface;
using ThresholdModeling;

namespace TTServer
{
    public class AuthenManagement
    {
        private static AuthenManagement _instance = null;

        public static AuthenManagement getInstance()
        {
            if (_instance == null)
            {
                _instance = new AuthenManagement();
            }

            return _instance;
        }

        public static void RenewInstance()
        {
            _instance = new AuthenManagement();
        }

        private enum AuthenticationType
        {
            TYPE_1 = 1,
            TYPE_2 = 2
        }

        private k_n_ThresholdModel _nMemberSystem = null;
        public bool isKeyMemberAuthenticated = false;
        //public k_n_ThresholdModel NMemberSystem
        //{
        //    get { return _nMemberSystem; }
        //    set { _nMemberSystem = value; }
        //}

        private List<CNMSMember> _authenticatingMems = new List<CNMSMember>();

        private List<CNMSMember> _authenticatedMems = new List<CNMSMember>();

        private MembersGroup _currentActiveGroup = null;

        public MembersGroup CurrentActiveGroup
        {
            get { return _currentActiveGroup; }
            set 
            {
                if (value != null)
                    _currentActiveGroup = new MembersGroup(value);
                else
                    _currentActiveGroup = null;
            }
        }

        private int _authenType = 1;

        public int AuthenType
        {
            get { return _authenType; }
            set { _authenType = value; }
        }

        //private long _fakeSecret = 0;

        //public long FakeSecret
        //{
        //    get { return _fakeSecret; }
        //    set { _fakeSecret = value; }
        //}

        public bool isAvailable()
        {
            if (_nMemberSystem != null)
            {
                return true;
            }

            return false;
        }

        #region For Test N_Members System
        //public long GenerateFakeDataFor_nMemberSystem(ref List<MemberInfo> members)
        //{
        //    //_nMemberSystem = new k_n_ThresholdModel(5, 3);
        //    //members = _nMemberSystem.ConstructSecret();

        //    //String memStr = "";
        //    //foreach (MemberInfo info in members)
        //    //{
        //    //    memStr += info.M.ToString() + " " + info.I.ToString() + " " + info.Mi.ToString() + "\n";
        //    //}

        //    //String path = OtherUtils.getPath() + @"\MembersInfo.txt";
        //    //OtherUtils.WriteFile(path, memStr);
        //    ////_authenticatingMems.Add(members[0]);
        //    ////_authenticatingMems.Add(members[1]);
        //    ////_authenticatingMems.Add(members[2]);

        //    //return _nMemberSystem.Secret1;
        //}

        private List<MemberInfo> ExtractMemberInfoList(List<CNMSMember> list)
        {
            List<MemberInfo> resultList = new List<MemberInfo>();

            for (int i = 0; i < list.Count; i++)
            {
                resultList.Add(list[i].info);
            }

            return resultList;
        }

        //public void TestRecoverySecret()
        //{
        //    //List<MemberInfo> list = ExtractMemberInfoList(_authenticatingMems);

        //    //FakeSecret = _nMemberSystem.RecoverySecret_1(list);
        //}
        #endregion

        public void GenerateInfoForMembers()
        {
            //try
            //{
            //    List<MemberInfo> result = _nMemberSystem.ConstructSecret();
            //    String members = "";
            //    foreach (MemberInfo info in result)
            //    {
            //        members += info.M.ToString() + " " + info.I.ToString() + " " + info.Mi.ToString() + "\n";
            //    }

            //    String path = OtherUtils.getPath() + @"\MembersInfo.txt";
            //    OtherUtils.WriteFile(path, members);

            //}
            //catch (Exception ex)
            //{
            //    throw ex;
            //}
        }

        private bool Authenticate()
        {
            List<MemberInfo> list = ExtractMemberInfoList(_authenticatingMems);

            return _nMemberSystem.RecoverySecretAndChecking(list);
        }

        private String AuthenticateForType2()
        {
            List<MemberInfo> list = ExtractMemberInfoList(_authenticatingMems);

            return _nMemberSystem.RecoverySecret(list);
        }

        //public void ConstructThresholdPattern()
        //{
        //    String path = Directory.GetCurrentDirectory() + @"\Data\ThresholdPattern.txt";
        //    try
        //    {
        //        if (!File.Exists(path))
        //        {
        //            k_n_ThresholdModel newknTM = new k_n_ThresholdModel();
        //            newknTM.ConstructThresholdPattern();
        //            String pattern = "";
        //            foreach (int threshold in newknTM.ThresholdPattern)
        //            {
        //                pattern += threshold.ToString() + "\n";
        //            }

        //            StreamWriter sw = new StreamWriter(path);
        //            sw.Write(pattern);
        //            sw.Close();
        //        }
        //    }
        //    catch (IOException ex)
        //    {
        //        throw ex;
        //    }

        //}

        //private void LoadThresholdPattern()
        //{
        //    String path = Directory.GetCurrentDirectory() + @"\Data\ThresholdPattern.txt";
        //    try
        //    {
        //        if (!File.Exists(path))
        //        {
        //            this._nMemberSystem.ConstructThresholdPattern();
        //            String pattern = "";
        //            foreach (int threshold in this._nMemberSystem.ThresholdPattern)
        //            {
        //                pattern += threshold.ToString() + "\n";
        //            }

        //            StreamWriter sw = new StreamWriter(path);
        //            sw.Write(pattern);
        //            sw.Close();
        //        }
        //        else
        //        {
        //            List<int> thresholdPattern = new List<int>();
        //            StreamReader sr = new StreamReader(path);
        //            while (!sr.EndOfStream)
        //            {
        //                thresholdPattern.Add(int.Parse(sr.ReadLine()));
        //            }
        //            sr.Close();

        //            this._nMemberSystem.GetPattern(thresholdPattern);
        //        }
        //    }
        //    catch (IOException ex)
        //    {
        //        throw ex;
        //    }

        //}

        public void StartThresholdModel(MembersGroup group)
        {
            this._nMemberSystem = new k_n_ThresholdModel(group.NMembers, group.KMembers, group.HashSecret, group.M);
            //LoadThresholdPattern();
        }

        public int AuthenticateNewMember(MemberInfo newMember, IClientInfoProvider clientInfoProvider)
        {
            if (this._currentActiveGroup == null)
            {
                return -10;
            }

            int key = this._currentActiveGroup.K_ExpMod;
            int prime = this._currentActiveGroup.P_ExpMod;
            int I_mes = Cryptography.ExponentCryptography_Decrypt(newMember.I, key, prime);
            int mi_mes = Cryptography.ExponentCryptography_Decrypt(newMember.mi, key, prime);
            newMember.I = I_mes;
            newMember.mi = mi_mes;

            CNMSMember new_NMS_member = new CNMSMember(clientInfoProvider, newMember);

            try
            {
                int type = 0;

                if (_nMemberSystem != null)
                {
                    if (_authenticatedMems.Count == _nMemberSystem.N)
                    {
                        type = 10; //All n members has authenticated
                    }
                    else
                    {
                        int k = _nMemberSystem.K;

                        _authenticatingMems.Add(new_NMS_member);

                        switch((AuthenticationType)this._authenType)
                        {
                            case AuthenticationType.TYPE_1:
                                type = AuthenticateNewMember_Type1(k);
                                break;
                            case AuthenticationType.TYPE_2:
                                type = AuthenticateNewMember_Type2(k);
                                break;
                        }

                    }
                }

                processResultByTheClient(clientInfoProvider, type);

                return type;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

        private int AuthenticateNewMember_Type1(int k)
        {
            int type = 0;
            CNMSMember new_NMS_member = _authenticatingMems[_authenticatingMems.Count - 1];
            if (_authenticatingMems.Count == k)
            {
                bool isSuc = Authenticate();

                if (isSuc)
                {
                    if (_authenticatedMems.Count == 0)  // Chưa có k thành viên được xác thực
                    {
                        _authenticatedMems.AddRange(_authenticatingMems);
                        type = 1; //Authenticate successfully first k member
                    }
                    else if (_authenticatedMems.Count >= k) // Có nhiều hơn hay bằng k thành viên được chứng thực
                    {
                        _authenticatedMems.Add(new_NMS_member);
                        type = 2; //Authenticate successfully new member.
                    }

                    //*
                    // Đảm bảo khi chứng thực thành công k thành viên,
                    // sẽ luôn có k -1 thành viên đợi sẵn thành viên mới tới
                    if (_authenticatingMems.Count == k)
                    {
                        _authenticatingMems.RemoveAt(k - 1);
                    }
                }
                else
                {
                    if (_authenticatedMems.Count == 0)// Chưa có k thành viên được xác thực
                    {
                        type = -1; //Fail to authenticate first k members. Request transfer first k members info again
                    }
                    else
                    {
                        type = -2; //Fail to authenticate new member. Request transfer newMember info again
                    }
                }
            }
            else
            {
                type = 0; //Not enough k member
            }
            return type;
        }

        private int AuthenticateNewMember_Type2(int k)
        {
            int type = 0;
            if (_authenticatingMems.Count == k + 1)
            {
                CNMSMember last = _authenticatingMems[_authenticatingMems.Count - 1];
                _authenticatingMems.RemoveAt(_authenticatingMems.Count - 1);
                String firstSecret = AuthenticateForType2();

                CNMSMember preLast = _authenticatingMems[_authenticatingMems.Count - 1];
                _authenticatingMems.RemoveAt(_authenticatingMems.Count - 1);
                _authenticatingMems.Add(last);
                String secondSecret = AuthenticateForType2();

                bool isSuc = firstSecret == null || secondSecret == null ? false : firstSecret.Equals(secondSecret);

                if (isSuc)
                {
                    if (_authenticatedMems.Count == 0)  // Chưa có k thành viên được xác thực
                    {
                        _authenticatedMems.AddRange(_authenticatingMems);
                        _authenticatedMems.Add(preLast);
                        type = 1; //Authenticate successfully first k member
                    }
                    else if (_authenticatedMems.Count > k + 1) // Có nhiều hơn (k + 1) thành viên được chứng thực
                    {
                        _authenticatedMems.Add(last);
                        type = 2; //Authenticate successfully new member.
                    }

                    //*
                    // Đảm bảo khi chứng thực thành công k thành viên,
                    // sẽ luôn có k thành viên đợi sẵn thành viên mới tới
                    if (_authenticatingMems.Count == k + 1)
                    {
                        _authenticatingMems.RemoveAt(k);
                    }
                }
                else
                {
                    if (_authenticatedMems.Count == 0)// Chưa có k thành viên được xác thực
                    {
                        type = -1; //Fail to authenticate first k members. Request transfer first k members info again
                    }
                    else
                    {
                        type = -2; //Fail to authenticate new member. Request transfer newMember info again
                    }
                }
            }
            else
            {
                type = 0; //Not enough (k + 1) member
            }
            return type;
        }

        private void processResultByTheClient(IClientInfoProvider clientInfoProvider, int type)
        {
            switch (type)
            {
                case -2:
                    {
                        SendMessageToAll("new member authentification failed");

                        SendMessageToClient(clientInfoProvider, NMSAuthentificationResponseMessage.TYPE_FAILED, 
                            "Authentication failed");

                        this.Reset();
                    }
                    break;
                case -1:
                    {
                        SendMessageToAllAuthentificatingMembers(NMSAuthentificationResponseMessage.TYPE_FAILED, 
                            "Authentication session failed");

                        this.Reset();

                    }
                    break;
                case 0:
                    {
                        String temp = "Need " + (_nMemberSystem.K - _authenticatingMems.Count ).ToString() + " member(s) more";
                        
                        SendMessageToClient(clientInfoProvider, NMSAuthentificationResponseMessage.TYPE_NORMAL, temp);
                        
                        SendMessageToAll(NMSAuthentificationResponseMessage.TYPE_NORMAL, temp);
                    }
                    break;
                case 1:
                    {
                        SendKeyToKMembers();
                    }
                    break;
                case 2:
                    {
                        SendKeyToTheNewMember(clientInfoProvider);
                    }
                    break;
            }
        }

        private void SendMessageToAll(byte result, String message)
        {
            NMSAuthentificationResponseMessage res =
                    new NMSAuthentificationResponseMessage( result, message);
            
            for (int i = 0; i < _authenticatedMems.Count; i++)
            {
                _authenticatedMems[i].clientInfoProvider.AddToMessageQueue(res);
            }
        }

        private void SendMessageToAllAuthentificatingMembers(byte result, String message)
        {
            NMSAuthentificationResponseMessage res =
                    new NMSAuthentificationResponseMessage(result, message);

            for (int i = 0; i < _authenticatingMems.Count; i++)
            {
                _authenticatingMems[i].clientInfoProvider.AddToMessageQueue(res);
            }
        }

        private void SendMessageToAll(String message)
        {
            NotifyMessage nm =
                    new NotifyMessage(message);

            for (int i = 0; i < _authenticatedMems.Count; i++)
            {
                _authenticatedMems[i].clientInfoProvider.AddToMessageQueue(nm);
            }
        }

        private void SendMessageToClient(IClientInfoProvider clientInfoProvider, byte result, String message)
        {
            NMSAuthentificationResponseMessage res =
                    new NMSAuthentificationResponseMessage(result, message);

            clientInfoProvider.AddToMessageQueue(res);
        }

        private void SendMessageToClient(IClientInfoProvider clientInfoProvider, String message)
        {
            NotifyMessage nm =
                    new NotifyMessage(message);

            clientInfoProvider.AddToMessageQueue(nm);
        }

        private void SendKeyToKMembers()
        {
            for (int i = 0; i < _authenticatedMems.Count; i++)
            { 
                String key = OtherUtils.GenerateTemporaryKey();

                IClientInfoProvider clientInfoProvider = _authenticatedMems[i].clientInfoProvider;

                clientInfoProvider.Authority = AuthorityConstant.MEMBER;
                clientInfoProvider.TemporaryKey = key;

                NMSAuthentificationResponseMessage res = new NMSAuthentificationResponseMessage(
                    NMSAuthentificationResponseMessage.TYPE_SUCCESSFUL,
                    key);

                clientInfoProvider.AddToMessageQueue(res);
                
            }
        }

        private void SendKeyToTheNewMember(IClientInfoProvider clientInfoProvider)
        {
            String key = OtherUtils.GenerateTemporaryKey();

            clientInfoProvider.Authority = AuthorityConstant.MEMBER;
            clientInfoProvider.TemporaryKey = key;

            NMSAuthentificationResponseMessage res = new NMSAuthentificationResponseMessage(
                    NMSAuthentificationResponseMessage.TYPE_SUCCESSFUL,
                    key);

            clientInfoProvider.AddToMessageQueue(res);
        }

        public bool Reset()
        {

            for (int i = 0; i < _authenticatedMems.Count; i++)
            {
                IClientInfoProvider clientInfoProvider = _authenticatedMems[i].clientInfoProvider;

                AuthorityResetMessage arm = new AuthorityResetMessage();

                clientInfoProvider.AddToMessageQueue(arm);
            }

            _authenticatedMems.Clear();
            _authenticatingMems.Clear();

            return true;
        }
    }
}
